package im.composer.media.sound.codec.seek;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.tritonus.share.sampled.FloatInputStream;

public abstract class AbstractSeekSupport {

	private static final ExecutorService es = Executors.newCachedThreadPool((r) -> {
		Thread t = new Thread(r);
		t.setDaemon(true);
		t.setPriority(Thread.MIN_PRIORITY);
		return t;
	});

	public enum SeekSupportState {
		UNINITED, SUPPORT, UNSUPPORTED, BUILDING
	}

	protected final Path path;
	protected final Path path1;
	protected FloatInputStream fis;
	protected NavigableMap<Long, Long> pos_map = new TreeMap<>();
	protected SeekSupportState state = SeekSupportState.UNINITED;

	public AbstractSeekSupport(Path path) {
		super();
		this.path = path;
		String[] strs = path.toUri().getPath().split("/");
		String fn = strs[strs.length - 1];
		path1 = path.resolveSibling(fn + ".seek.bin");
		boolean seek_index_exists = checkSeekIndex();
		if (seek_index_exists) {
			try {
				readSeekIndex();
			} catch (Exception e) {
			}
		}
		if (pos_map.isEmpty()) {
			state = SeekSupportState.BUILDING;
			es.submit(buildWorkder());
		} else {
			state = SeekSupportState.SUPPORT;
		}
	}

	public abstract void seek(long sample_position);

	protected abstract Callable<Void> buildWorkder();

	protected boolean checkSeekIndex() {
		return Files.exists(path1) && Files.isReadable(path1);
	}

	protected void readSeekIndex() throws Exception {
		pos_map.clear();
		DataInputStream in = new DataInputStream(new GZIPInputStream(Files.newInputStream(path1), 1024));
		while (in.available() > 0) {
			long key = in.readLong();
			long val = in.readLong();
			pos_map.put(key, val);
		}
		in.close();
	}

	protected void writeSeekIndex() throws Exception {
		DataOutputStream out = new DataOutputStream(new GZIPOutputStream(Files.newOutputStream(path1), 1024, true));
		for (Entry<Long, Long> entry : pos_map.entrySet()) {
			out.writeLong(entry.getKey());
			out.writeLong(entry.getValue());
		}
		out.flush();
		out.close();
	}

	public void setOutput(FloatInputStream fis) {
		this.fis = fis;
	}

	public boolean isEnabled() {
		return state == SeekSupportState.BUILDING || state == SeekSupportState.SUPPORT;
	}

}
